﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Emlyn.AudioToVideo.FileUploadManager
{
    public class FileToUpload
    {
        #region Private Members
        string _userID;
        string _fileToUploadID;

        private void CreateFolderStructure(string aUserID, string aFileToUploadID)
        {
            string lfileToUploadFolder = FileToUploadFolder(aUserID, aFileToUploadID);

            using (new FileToUploadScope(aUserID, aFileToUploadID))
            {
                if (!Directory.Exists(lfileToUploadFolder))
                    Directory.CreateDirectory(lfileToUploadFolder);
            }
        }

        private void DeleteFolderStructure(string aUserID, string aFileToUploadID)
        {
            string lfileToUploadFolder = FileToUploadFolder(aUserID, aFileToUploadID);

            using (new FileToUploadScope(aUserID, aFileToUploadID))
            {
                if (Directory.Exists(lfileToUploadFolder))
                    Directory.Delete(lfileToUploadFolder, true);
            }
        }

        #endregion

        #region constructors

        public FileToUpload(string aUserID)
        {
            if (String.IsNullOrEmpty(aUserID))
                throw new ArgumentNullException("aUserID");

            _userID = aUserID;

            _fileToUploadID = Guid.NewGuid().ToString();

            CreateFolderStructure(_userID, _fileToUploadID);
        }

        public FileToUpload(string aUserID, string aFileToUpload)
        {
            if (String.IsNullOrEmpty(aUserID))
                throw new ArgumentNullException("aUserID");

            using (new FileToUploadScope(aUserID, aFileToUpload))
            {
                if (!Exists(aUserID, aFileToUpload))
                {
                    CreateFolderStructure(aUserID, aFileToUpload);
                }
            }

            _userID = aUserID;

            _fileToUploadID = aFileToUpload;
        }

        #endregion

        public static string BaseFolder()
        {
            return Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                "FileUploadManager"
                );
        }

        public static string UserFolder(string aUserID)
        {
            if (String.IsNullOrEmpty(aUserID))
                throw new ArgumentNullException("aUserID");

            return Path.Combine(
                BaseFolder(),
                "User-" + aUserID
                );
        }

        public static string FileToUploadFolder(string aUserID, string aFileToUploadID)
        {
            if (String.IsNullOrEmpty(aFileToUploadID))
                throw new ArgumentNullException("aFileToUpload");

            return Path.Combine(
                UserFolder(aUserID),
                "File-" + aFileToUploadID
                );
        }

        public static bool Exists(string aUserID, string aFileToUploadID)
        {
            return Directory.Exists(FileToUploadFolder(aUserID, aFileToUploadID));
        }

        public string UserID { get { return _userID; } }
        public string FileToUploadID { get { return _fileToUploadID; } }

        public void AcceptFragment(UInt32 aFragmentNumber, byte[] aFragment)
        {
            string lfragmentFileName =
                Path.Combine(
                    FileToUploadFolder(UserID, FileToUploadID),
                    "fragment-" + aFragmentNumber.ToString() + ".raw"
                    );

            File.WriteAllBytes(lfragmentFileName, aFragment);
        }

        public void CompleteFile(UInt32 aTotalNumberOfFragments, byte aChecksum)
        {
            byte lcalculatedChecksum = 0;
            string loutputFileName =
                Path.Combine(
                    FileToUploadFolder(UserID, FileToUploadID),
                    FileToUploadID + ".complete"
                    );

            using (FileStream loutputFileStream = new FileStream(loutputFileName, FileMode.Create))
            {
                for (int lfragmentNumber = 0; lfragmentNumber < aTotalNumberOfFragments; lfragmentNumber++)
                {
                    string lfileName =
                        Path.Combine(
                            FileToUploadFolder(UserID, FileToUploadID),
                            "fragment-" + lfragmentNumber.ToString() + ".raw"
                            );

                    if (!File.Exists(lfileName))
                        throw new Exception("Can't find fragment " + lfragmentNumber.ToString());

                    byte[] lfragmentContent = File.ReadAllBytes(lfileName);

                    foreach (byte lfragmentByte in lfragmentContent)
                    {
                        lcalculatedChecksum += lfragmentByte;
                    };

                    loutputFileStream.Write(lfragmentContent, 0, lfragmentContent.Length);
                }
            }

            if (lcalculatedChecksum != aChecksum)
            {
                File.Delete(loutputFileName);

                throw new Exception("Checksums don't match");
            }
        }

        public void Cancel()
        {
            DeleteFolderStructure(UserID, FileToUploadID);
        }
    }
}
